use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use super::{bi::BI, enums::Direction};

/// 中枢对象，主要用于辅助信号函数计算
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZS {
    /// 构成中枢的笔列表
    pub bis: Vec<BI>,
    /// 缓存，用户缓存
    pub cache: HashMap<String, serde_json::Value>,
}

impl ZS {
    /// 创建新的中枢
    pub fn new(bis: Vec<BI>) -> Self {
        Self {
            bis,
            cache: HashMap::new(),
        }
    }

    /// 标的代码
    pub fn symbol(&self) -> &str {
        &self.bis[0].symbol
    }

    /// 中枢开始时间
    pub fn sdt(&self) -> DateTime<Utc> {
        self.bis[0].sdt()
    }

    /// 中枢结束时间
    pub fn edt(&self) -> DateTime<Utc> {
        self.bis.last().unwrap().edt()
    }

    /// 中枢第一笔方向
    pub fn sdir(&self) -> Direction {
        self.bis[0].direction
    }

    /// 中枢倒一笔方向
    pub fn edir(&self) -> Direction {
        self.bis.last().unwrap().direction
    }

    /// 中枢上沿
    pub fn zg(&self) -> f64 {
        if self.bis.len() < 3 {
            return 0.0;
        }
        self.bis[..3].iter().map(|x| x.high()).fold(f64::INFINITY, f64::min)
    }

    /// 中枢下沿
    pub fn zd(&self) -> f64 {
        if self.bis.len() < 3 {
            return 0.0;
        }
        self.bis[..3].iter().map(|x| x.low()).fold(f64::NEG_INFINITY, f64::max)
    }

    /// 中枢中轴
    pub fn zz(&self) -> f64 {
        self.zd() + (self.zg() - self.zd()) / 2.0
    }

    /// 中枢最高点
    pub fn gg(&self) -> f64 {
        self.bis.iter().map(|x| x.high()).fold(f64::NEG_INFINITY, f64::max)
    }

    /// 中枢最低点
    pub fn dd(&self) -> f64 {
        self.bis.iter().map(|x| x.low()).fold(f64::INFINITY, f64::min)
    }

    /// 中枢是否有效
    pub fn is_valid(&self) -> bool {
        if self.zg() < self.zd() {
            return false;
        }

        for bi in &self.bis {
            // 中枢内的笔必须与中枢的上下沿有交集
            let zg = self.zg();
            let zd = self.zd();
            let bi_high = bi.high();
            let bi_low = bi.low();

            if (zg >= bi_high && bi_high >= zd)
                || (zg >= bi_low && bi_low >= zd)
                || (bi_high >= zg && zg > zd && zd >= bi_low)
            {
                continue;
            } else {
                return false;
            }
        }

        true
    }

    /// 获取缓存值
    pub fn get_cache<T>(&self, key: &str) -> Option<T>
    where
        T: for<'de> Deserialize<'de>,
    {
        self.cache.get(key).and_then(|v| serde_json::from_value(v.clone()).ok())
    }

    /// 设置缓存值
    pub fn set_cache<T>(&mut self, key: &str, value: T)
    where
        T: Serialize,
    {
        if let Ok(v) = serde_json::to_value(value) {
            self.cache.insert(key.to_string(), v);
        }
    }
}

impl std::fmt::Display for ZS {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "ZS(sdt={}, sdir={}, edt={}, edir={}, len_bis={}, zg={:.2}, zd={:.2}, gg={:.2}, dd={:.2}, zz={:.2})",
            self.sdt(),
            self.sdir(),
            self.edt(),
            self.edir(),
            self.bis.len(),
            self.zg(),
            self.zd(),
            self.gg(),
            self.dd(),
            self.zz()
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::objects::{bar::NewBar, enums::{Direction, Mark, Freq}, fx::FX};

    #[test]
    fn test_zs() {
        let dt = Utc::now();
        
        // 创建测试用的BI
        let new_bar1 = NewBar::new(
            "000001.SZ".to_string(),
            1,
            dt,
            Freq::Day,
            10.0,
            11.0,
            12.0,
            9.0,
            1000.0,
            11000.0,
        );

        let new_bar2 = NewBar::new(
            "000001.SZ".to_string(),
            2,
            dt,
            Freq::Day,
            11.0,
            10.0,
            12.0,
            9.0,
            1000.0,
            11000.0,
        );

        let new_bar3 = NewBar::new(
            "000001.SZ".to_string(),
            3,
            dt,
            Freq::Day,
            10.0,
            9.0,
            12.0,
            8.0,
            1000.0,
            11000.0,
        );

        let fx_a = FX::new(
            "000001.SZ".to_string(),
            dt,
            Mark::D,
            12.0,
            8.0,
            9.0,
            vec![new_bar1.clone()],
        );

        let fx_b = FX::new(
            "000001.SZ".to_string(),
            dt,
            Mark::G,
            13.0,
            7.0,
            12.0,
            vec![new_bar3.clone()],
        );

        let bi = BI::new(
            "000001.SZ".to_string(),
            fx_a,
            fx_b,
            vec![],
            Direction::Up,
            vec![new_bar1, new_bar2, new_bar3],
        );

        let zs = ZS::new(vec![bi]);

        assert_eq!(zs.symbol(), "000001.SZ");
        assert_eq!(zs.zg(), 0.0); // 因为只有一个BI，所以zg为0
        assert_eq!(zs.zd(), 0.0); // 因为只有一个BI，所以zd为0
    }
} 